#include "umode.h"
#include <fstream>



UMode::UMode() : _max_cands(64), _min_freq(50), _cands_cnt(0)
{
}

void UMode::Init()
{
    for(auto w : uwords) 
    {
        insert_word(_root, w.first.c_str(), w.second);
    }   
}

void UMode::insert_word(WordNode &node, const char *w, uint32_t freq)
{
    if('\0' == *w)
    {
        node.freq = freq;
        return; 
    }
#if 0 //no need to check, map operator [] will auto create an empty node if not created
    auto it = node.children.find(*w);
    if(node.children.end() == it)
    {
        node.children[*w] = WordNode();
    }
#endif
    return insert_word(node.children[*w], w+1, freq);
}

int UMode::LoadDict(const std::string &dict)
{
    std::ifstream in(dict, std::ios::binary|std::ios::in);
    if(!in.is_open())
    {
        printf("Failed to open dict file: %s\n", dict.c_str());
        return -1;
    }
    std::string line;
    char word[32];
    uint32_t freq = 100;
    while(std::getline(in, line))
    {
        if (line.empty() || line[0] == '#') continue;
        int mcnt = sscanf(line.c_str(), " %32[a-zA-Z]%u", word, &freq);
        if(mcnt < 1 )
        {
            printf("Invalid line: %s\n", line.c_str());
            continue;
        }
        insert_word(_root, word, freq);
    }
    return 0;
}

void UMode::insert_cand(const std::string &cand, uint32_t freq)
{
    if(freq < _min_freq)
    {
        return;
    }
    _cands_map[freq].push_back(cand);
    _cands_cnt++;
    if(_cands_cnt > _max_cands)
    {
        _cands_cnt--;
        auto it = _cands_map.begin();
        it->second.pop_back();
        if(it->second.empty())
        {
            _cands_map.erase(it->first);
        }
    }
}

void UMode::search(const WordNode &node, std::string &w)
{
    
    insert_cand(w, node.freq);
    for(auto it = node.children.begin(); it != node.children.end(); it++)
    {
        w.push_back(it->first);
        search(it->second, w);
        w.pop_back();
    }
}
void UMode::search(const WordNode &node, const char *p)
{
    if('\0' == *p)
    {
        search(node, _prefix);
        return;
    }
    auto it = node.children.find(*p);
    if(it != node.children.end())
    {
        search(it->second, p+1);
    }

}
const UMode::Candidates &UMode::Decode(const std::string &ustr)
{
    for(auto c : ustr)
    {
        if('\b' == c)
        {
            if(_prefix.size() > 0)
            {
                _prefix.pop_back();
            }
        }
        else
        {
            _prefix.push_back(c);
        }
    }
    _cands_map.clear();
    _cands_cnt = 0;   
    _cands.clear();
    search(_root, _prefix.c_str());
    for(auto it = _cands_map.rbegin(); it!=_cands_map.rend(); it++)
    {
        //printf("cand freq:%u\n", it->first);
        for(auto & cand : it->second)
        {
            _cands.push_back(cand);
        }
    }
    if(_cands.empty())
    {
        _cands.push_back(_prefix);
    }
    return _cands;
}

void UMode::Clear()
{
    _prefix.clear();
    _cands.clear();
    _cands_map.clear();
    _cands_cnt = 0;
}

const std::string UMode::Select(int idx)
{
    auto commit =  _cands[idx];
    Clear();
    return commit;
}


std::pair<std::string, std::uint32_t> UMode::uwords[127] =
{
    {"us", 200},
    {"up", 200},
    {"use", 200},
    {"used", 199},
    {"user", 199},
    {"under", 199},
    {"university", 199},
    {"united", 198},
    {"using", 198},
    {"uk", 198},
    {"usa", 197},
    {"u", 196},
    {"users", 195},
    {"until", 194},
    {"updated", 193},
    {"upon", 193},
    {"url", 193},
    {"update", 193},
    {"usually", 192},
    {"unit", 192},
    {"usr", 189},
    {"union", 189},
    {"unique", 188},
    {"understand", 187},
    {"useful", 186},
    {"units", 186},
    {"uses", 186},
    {"unless", 185},
    {"usb", 185},
    {"understanding", 184},
    {"updates", 184},
    {"un", 184},
    {"unknown", 183},
    {"username", 180},
    {"upgrade", 180},
    {"upper", 179},
    {"urban", 179},
    {"universal", 179},
    {"ups", 175},
    {"ultimate", 174},
    {"usd", 173},
    {"utilities", 173},
    {"utility", 172},
    {"utah", 172},
    {"usage", 170},
    {"utc", 169},
    {"unlimited", 168},
    {"unix", 168},
    {"upcoming", 167},
    {"ultra", 166},
    {"universe", 166},
    {"und", 165},
    {"unable", 164},
    {"ut", 162},
    {"upload", 162},
    {"universities", 162},
    {"uniprotkb", 162},
    {"unsubscribe", 161},
    {"upskirt", 161},
    {"usual", 159},
    {"unfortunately", 159},
    {"underground", 158},
    {"undergraduate", 155},
    {"ukraine", 154},
    {"uniform", 153},
    {"urw", 153},
    {"usc", 151},
    {"unlike", 150},
    {"unusual", 148},
    {"understood", 146},
    {"underlying", 143},
    {"ultimately", 141},
    {"underwear", 141},
    {"untitled", 140},
    {"um", 139},
    {"upgrades", 138},
    {"upskirts", 137},
    {"unto", 137},
    {"unavailable", 136},
    {"uv", 136},
    {"utils", 136},
    {"undefined", 135},
    {"unemployment", 135},
    {"unions", 135},
    {"univ", 133},
    {"unsigned", 133},
    {"updating", 132},
    {"unity", 132},
    {"uploaded", 130},
    {"unlikely", 130},
    {"uncle", 128},
    {"uganda", 128},
    {"undertaken", 127},
    {"ur", 127},
    {"uc", 126},
    {"unified", 126},
    {"uruguay", 124},
    {"ugly", 124},
    {"une", 123},
    {"uncertainty", 122},
    {"una", 120},
    {"unexpected", 119},
    {"urgent", 118},
    {"usgs", 118},
    {"uzbekistan", 116},
    {"ui", 116},
    {"uw", 115},
    {"utilization", 114},
    {"uh", 114},
    {"undo", 113},
    {"uri", 113},
    {"utilize", 112},
    {"unlock", 112},
    {"unauthorized", 110},
    {"uni", 110},
    {"ul", 110},
    {"upc", 109},
    {"usda", 108},
    {"urls", 108},
    {"undertake", 107},
    {"upset", 106},
    {"unnecessary", 106},
    {"upgrading", 105},
    {"usps", 104},
    {"urge", 102},
    {"ultram", 101},
    {"unwrap", 101}
};
